#include <ArduinoJson.h>
#include <Preferences.h>
#include "SimpleMap.h"
#include "blefunc.h"
#include "utils.h"
#include "global.h"
#include "List.h"
#include "queue.h"

// BLEUUID serviceUUID("0000fff0-0000-1000-8000-00805f9b34fb");

BLEUUID serviceUUID("0000fff0-0000-1000-8000-00805f9b34fb");
BLEUUID readUUID("0000fff1-0000-1000-8000-00805f9b34fb");
BLEUUID writeUUID("0000fff2-0000-1000-8000-00805f9b34fb");

BLEAdvertisedDevice *myDevice;

static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)
{
  Serial.print("Notify callback for characteristic ");
  Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
  String dataString;
  for (size_t i = 0; i < length; i++)
  {
    dataString += (char)pData[i]; // 假设数据是文本数据
  }

  printStr("ble 接收到的数据", dataString);
  JsonDocument doc;
  DeserializationError error1 = deserializeJson(doc, dataString);
  if (error1)
  {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error1.f_str());
    return;
  }

  std::string address = pBLERemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
  printStr("address->", String(address.c_str()));
  String macStr = String(convertToUpperCaseMacAddress(address.c_str()));
  RemoteBleData *remoteData = getRemoteBleDataMap()->get(macStr);
  if (remoteData == nullptr)
  {
    remoteData = new RemoteBleData();
    getRemoteBleDataMap()->put(macStr, remoteData);
  }
  List<RemoteDataBean> list = remoteData->getRemoteDataList();

   remoteData->setRemoteDataListStr(dataString);

  JsonArray jsonArray = doc.as<JsonArray>();
  for (size_t i = 0; i < jsonArray.size(); i++)
  {
    float humiValue = jsonArray["humiValue"];
    float vcc = jsonArray["vcc"];
    long timestamp = jsonArray["timestamp"];
    RemoteDataBean remoteData(humiValue, vcc, timestamp);
    list.add(remoteData);
  }
  remoteData->setRemoteDataList(list);


}

class MyClientCallback : public BLEClientCallbacks
{

  RemoteBle *remoteBle;

public:
  MyClientCallback()
  {
  }

  MyClientCallback(RemoteBle *remote)
  {
    remoteBle = remote;
  }

  void onConnect(BLEClient *pclient)
  {

    std::string addr = pclient->getPeerAddress().toString();
    String str = String(addr.c_str());
    printStr("onConnect", str);
  }

  void onDisconnect(BLEClient *pclient)
  {
    Serial.println("onDisconnect");

    std::string addr = pclient->getPeerAddress().toString();

    String str = String(addr.c_str());

    printStr("onDisconnect", str);
  }
};

bool RemoteBle::isConnect()
{
  if (pClient == nullptr)
  {
    return false;
  }
  return pClient->isConnected();
}

bool RemoteBle::connect()
{

  if (myDevice == nullptr)
  {
    return false;
  }
  serviceUUID = BLEUUID::fromString("0000fff0-0000-1000-8000-00805f9b34fb");
  readUUID = BLEUUID::fromString("0000fff1-0000-1000-8000-00805f9b34fb");
  writeUUID = BLEUUID::fromString("0000fff2-0000-1000-8000-00805f9b34fb");
  BLEClient *pClient = BLEDevice::createClient();
  Serial.println(" - Created client");
  pClient->setClientCallbacks(new MyClientCallback(this));

  pClient->connect(myDevice);
  Serial.println(" - Connected to server");
  pClient->setMTU(517);
  BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr)
  {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");
  // Obtain a reference to the characteristic in the service of the remote BLE server.
  readCharacter = pRemoteService->getCharacteristic(readUUID);

  if (readCharacter == nullptr)
  {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(readUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if (readCharacter->canRead())
  {
    std::string value = readCharacter->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  if (readCharacter->canNotify())
  {
    readCharacter->registerForNotify(notifyCallback);
  }

  writeCharacter = pRemoteService->getCharacteristic(writeUUID);

  if (writeCharacter == nullptr)
  {
    Serial.print("写通道不存在");
    Serial.println(writeUUID.toString().c_str());

    return false;
  }
  else
  {
    Serial.print("写通道存在");
  }

  Serial.println(" - Found our characteristic");
  // Read the value of the characteristic.
  if (writeCharacter->canWrite())
  {
    writeCharacter->writeValue("sssss", true);
    Serial.print("向写通道写值:sssss ");
  }

  return true;
}

String RemoteBle::getRemoteAddress()
{
  if (btDevice == nullptr)
  {
    return String("");
  }

  // String address =   btDevice->address;
  // printStr("adddddresss",address);
  // String mac = parseMacAddress(address);
  // printStr("fffff",mac);

  // printStr("address",btDevice->address);
  return btDevice->address;
}

bool RemoteBle::readValue(String &s)
{

  printStr("canRead");
  // Read the value of the characteristic.
  if (readCharacter->canRead())
  {
    std::string value = readCharacter->readValue();

    printStr("读取到值", String(value.c_str()));
    s = String(value.c_str());
    return true;
  }
  printStr("no data");
  return false;
}

bool RemoteBle::writeValue(String value)
{

  if (writeCharacter == nullptr)
  {
    return false;
  }
  if (writeCharacter->canWrite())
  {

    std::string val = std::string(value.c_str());
    writeCharacter->writeValue(val, true);

    printStr("向写通道写值:", value);
    return true;
  }
  else
  {
    printStr("此通道不可写");
    return false;
  }
}
// 重载==运算符
bool RemoteBle::operator==(const RemoteBle &other) const
{

  return pClient->getPeerAddress().equals(other.pClient->getPeerAddress());
}

int scanTime = 10; // In seconds
BLEScan *pBLEScan;

#define SERVICE_UUID "4FAFC201-1FB5-459E-8FCC-C5C9C331914B" // C5C9C331914B
#define CHARACTERISTIC_UUID "BEB5483E-36E1-4688-B7F5-EA07361B26A8"

BLEServer *pServer = NULL;
BLECharacteristic *pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0;

bool isSetupBleValue = false;
bool isAdvertisingVal = false;
bool isNeedAdvertisingVal = false;

static boolean doConnect = false;
static boolean connected = false;

class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
{
  void onResult(BLEAdvertisedDevice advertisedDevice)
  {

    Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());

    // We have found a device, let us now see if it contains the service we are looking for.
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID))
    {

      BLEDevice::getScan()->stop();
      Serial.printf("找到了指定设备: %s \n", advertisedDevice.toString().c_str());

      // myDevice = new BLEAdvertisedDevice(advertisedDevice);
      // doConnect = true;

      String address = String(advertisedDevice.toString().c_str());
      String macAddress = parseMacAddress(address);
      if (getRemoteBleMap()->has(macAddress))
      {
        getRemoteBleMap()->remove(macAddress);
      }
      RemoteBle *remoteBle = new RemoteBle(advertisedDevice);

      getRemoteBleMap()->put(macAddress, remoteBle);
    }
  }
};

class MyServerCallbacks : public BLEServerCallbacks
{
  void onConnect(BLEServer *pServer)
  {
    Serial.println("ble onConnect");
     deviceConnected = true;
    // BLEDevice::startAdvertising();
  };

  void onDisconnect(BLEServer *pServer)
  {
     Serial.println("ble onDisconnect");
     deviceConnected = false;
  }
};
class MyCallbacks : public BLECharacteristicCallbacks
{
  void onWrite(BLECharacteristic *pCharacteristic)
  {
    Serial.println("****onWrite*****");
    const char *cstrValue = pCharacteristic->getValue().c_str();
    char dest[100];                             // 较小的缓冲区
    strncpy(dest, cstrValue, sizeof(dest) - 1); // 复制9个字符，并手动添加空字符
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, dest);
    Serial.println("dest:");
    Serial.println(dest);
    if (error)
    {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.f_str());
      return;
    }
    const char *code = doc["code"];

    if (strcmp(code, "device-active") == 0)
    {
      const char *account = doc["account"];
      Preferences preferences;
      preferences.begin("my-app", false);
      preferences.putString("account", account);
      setNeedSendActive(true);
    }

    if (strcmp(code, "config-wifi") == 0)
    {
      String ssid = doc["ssid"];
      String password = doc["password"];
      Serial.println(ssid);
      Serial.println(password);
      putPreferenceString("ssid_pro", ssid);
      putPreferenceString("password_pro", password);
      setSsid(ssid);
      setPassword(password);
      setWiFiConnected(false);
      setNeedConnectWiFi(true);
    }
  }
  void onRead(BLECharacteristic *pCharacteristic, esp_ble_gatts_cb_param_t *param)
  {
    Serial.println("*****onRead****");

  }
};
void sendNotify(String msg)
{
  if (deviceConnected)
  {

    pCharacteristic->setValue(msg.c_str());
    pCharacteristic->notify(); // 发送通知
    Serial.print("Notifying clients with: ");
    Serial.println(msg);
    // 你可以在这里添加逻辑来定期或基于某些条件发送消息
    // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
  }
};

String parseMacAddress(String data)
{
  // 给定的字符串
  //  String data = "Name: xxxb, Address: c5:91:79:21:84:c6, manufacturer data: 0000c591792184c6, serviceUUID: 0000fff0-0000-1000-8000-00805f9b34fb, rssi: -58";

  // 查找"Address: "的起始位置

  if (data == nullptr || data.isEmpty())
  {
    return String("");
  }
  int addressStartIndex = data.indexOf("Address: ") + strlen("Address: ");

  // 查找逗号（,）的位置，它标志着Address字段的结束
  int addressEndIndex = data.indexOf(",", addressStartIndex);

  // 提取Address字段的值
  String address = data.substring(addressStartIndex, addressEndIndex);

  // 输出Address字段的值
  Serial.print("Addresse的值: ");
  Serial.println(address);

  return address;
}

void readAllBleData()
{

  if (getRemoteBleMap() == nullptr || getRemoteBleMap()->size() == 0)
  {
    printStr("remoteBleMap 为空");
    return;
  }
  // printStr("remoteBleMap 不为空", remoteBleMap->size());
  for (int i = 0; i < getRemoteBleMap()->size(); i++)
  {
    RemoteBle *remote = getRemoteBleMap()->getData(i);
    // printStr("connect");
    if (true)
    {
      // printStr("准备读取蓝牙数据");
      String value;
      if (remote->readValue(value))
      {
        printStr("读取到远程蓝牙设备数据", value);
      }
      else
      {
        // printStr("没有数据");
      }
    }
    else
    {
      // printStr("没有连接");
    }
  }
}

void writeAllBleData()
{

  if (getRemoteBleMap() == nullptr || getRemoteBleMap()->size() == 0)
  {
    printStr("remoteBleMap 为空");
    return;
  }
  // printStr("remoteBleMap 不为空", remoteBleMap->size());
  for (int i = 0; i < getRemoteBleMap()->size(); i++)
  {
    RemoteBle *remote = getRemoteBleMap()->getData(i);
    // printStr("connect");
    if (true)
    {
      // printStr("准备读取蓝牙数据");
      String value;
      if (remote->writeValue("bbbbbbbb"))
      {
        printStr("发送到远程蓝牙设备数据", value);
      }
      else
      {
        // printStr("没有数据");
      }
    }
    else
    {
      // printStr("没有连接");
    }
  }
}

BLERemoteCharacteristic *pRemoteCharacteristic;
BLERemoteCharacteristic *pRemoteCharacteristic1;

bool connectToServer()
{
  Serial.print("Forming a connection to ");

  Serial.println(myDevice->getAddress().toString().c_str());

  BLEClient *pClient = BLEDevice::createClient();

  Serial.println(" - Created client");

  pClient->setClientCallbacks(new MyClientCallback());
  // Connect to the remove BLE Server.
  pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
  Serial.println(" - Connected to server");
  pClient->setMTU(517); // set client to request maximum MTU from server (default is 23 otherwise)

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr)
  {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");
  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(readUUID);

  if (pRemoteCharacteristic == nullptr)
  {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(readUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if (pRemoteCharacteristic->canRead())
  {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  if (pRemoteCharacteristic->canNotify())
  {
    pRemoteCharacteristic->registerForNotify(notifyCallback);
  }

  pRemoteCharacteristic1 = pRemoteService->getCharacteristic(writeUUID);

  if (pRemoteCharacteristic1 == nullptr)
  {
    Serial.print("写通道不存在");
    Serial.println(writeUUID.toString().c_str());

    return false;
  }
  else
  {
    Serial.print("写通道存在");
  }

  Serial.println(" - Found our characteristic");
  // Read the value of the characteristic.
  if (pRemoteCharacteristic1->canWrite())
  {
    pRemoteCharacteristic1->writeValue("sssss", true);
    Serial.print("向写通道写值:sssss ");
  }

  connected = true;
  return true;
}

void loopBle()
{

  if (isNeedStartAdvertising() && (!isAdvertising()))
  {
    if (!isSetupBleValue)
    {
      /// setupBle();
    }
    // startAdvertising(); // 暂时注释掉
    setNeedStartAdvertising(false);
  }

  if (!isSetupBleValue)
  {
    return;
  }

  // disconnecting
  // if (!deviceConnected && oldDeviceConnected)
  // {
  //   delay(500);                  // give the bluetooth stack the chance to get things ready
  //   pServer->startAdvertising(); // restart advertising
  //   Serial.println("start advertising");
  //   oldDeviceConnected = deviceConnected;
  // }
  // // connecting
  // if (deviceConnected && !oldDeviceConnected)
  // {
  //   // do stuff here on connecting
  //   oldDeviceConnected = deviceConnected;
  // }

   
   

   //上面这一段先注释掉，有没有问题待测试
  


  // printStr("bleloop");
  // readAllBleData();
  // writeAllBleData();
  // delay(1000);
}

void setupBle()
{
  String id = getEspId();
  String subId = id.substring(id.length() - 4);
  std::string bleName = "ZNJS_";
  std::string subid = std::string(subId.c_str());
  bleName.append(subid);

  BLEDevice::init(bleName);

  if(pServer!=nullptr){
    return;
  }
  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
      CHARACTERISTIC_UUID,
      BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE);

  // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
  // Create a BLE Descriptor
  pCharacteristic->addDescriptor(new BLE2902());
  pCharacteristic->setCallbacks(new MyCallbacks());

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
  isSetupBleValue = true;
  // Start the service
  pService->start(); // 暂时注释掉

    setupScan();

  // startScan();
}

void setupScan()
{
  pBLEScan = BLEDevice::getScan(); // create new scan
                                   // pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);   // active scan uses more power, but get results faster
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99); // less or equal setInterval value
}

void startScan()
{

  // put your main code here, to run repeatedly:
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  for (size_t i = 0; i < foundDevices.getCount(); i++)
  {
    BLEAdvertisedDevice device = foundDevices.getDevice(i);
    if (device.haveServiceUUID() && device.isAdvertisingService(serviceUUID))
    {
      String macAddress = String(convertToUpperCaseMacAddress(device.getAddress().toString().c_str()));
      printStr("找到的设备", macAddress);
      RemoteBle *remoteBle = getRemoteBleMap()->get(macAddress);
      if (remoteBle == nullptr)
      {
        printStr("找到的设备在Map中不存在");
        return;
      }

      if (getRemoteBleMap()->get(macAddress) != nullptr)
      {
        getRemoteBleMap()->get(macAddress)->setAdvertisedDevice(device);
        bool isConnect = getRemoteBleMap()->get(macAddress)->connect();
        if (isConnect)
        {
          printStr("蓝牙连接成功");
        }
        else
        {
          printStr("蓝牙连接失败");
        }
      }
    }
  }
  pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
}

bool isAdvertising()
{
  return isAdvertisingVal;
}
void setAdvertising(bool b)
{
  isAdvertisingVal = b;
}
void startAdvertising()
{

  BLEDevice::startAdvertising();
  Serial.println(" startAdvertising...");
  setAdvertising(true);
}

void stopAdvertising()
{
  BLEDevice::stopAdvertising();
  Serial.println("stopAdvertising ");
  setAdvertising(false);
}
void setNeedStartAdvertising(bool b)
{

  isNeedAdvertisingVal = b;
}
bool isNeedStartAdvertising()
{
  return isNeedAdvertisingVal;
}